﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Globalization;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Zip;

//using Timer = System.Windows.Forms.Timer;

namespace XFControls.Utils
{
    public enum HttpVerb
    {
        GET,
        POST,
        DELETE
    }

    public class PercentCompleteArgs : EventArgs
    {
        public PercentCompleteArgs(int percent)
        {
            Percent = percent;
        }

        public int Percent { get; set; }
    }

    public class SuccessEventArgs : EventArgs
    {
        public SuccessEventArgs(bool success)
        {
            Success = success;
        }

        public bool Success { get; set; }
    }

    public class RequestState
    {
        public HttpWebRequest Request { get; set; }
        public byte[] Data { get; set; }
        public EventHandler<SuccessEventArgs> OnComplete;

        public RequestState(HttpWebRequest request, EventHandler<SuccessEventArgs> complete, byte[] data)
            : this(request, complete)
        {
            Data = data;
        }

        public RequestState(HttpWebRequest request, EventHandler<SuccessEventArgs> complete)
        {
            Request = request;
            OnComplete = complete;
        }
    }

    public static class RequestUtil
    {
        public static string EncodeDictionary(Dictionary<string, string> dict, bool questionMark)
        {
            var sb = new StringBuilder();
            if (questionMark)
            {
                sb.Append("?");
            }

            bool first = true;
            foreach (var kvp in dict)
            {
                if (!first)
                {
                    sb.Append("&");
                }
                else
                {
                    first = false;
                }

                sb.Append(HttpUtility.UrlEncode(kvp.Key));
                sb.Append("=");
                sb.Append(HttpUtility.UrlEncode(kvp.Value));
            }

            return sb.ToString();
        }
    }

    public class WebDownloaderWrapper : IDisposable
    {
        public bool IsDownloading;
        public string Url { get; private set; }
        public Dictionary<string, string> PostData { get; set; }
        public event EventHandler<TextEventArgs> GetResults;
        public event EventHandler DownloadFileComplete;
        public List<XFGraphics.GetImageDelegate> GetImageCallbacks = new List<XFGraphics.GetImageDelegate>();
        public object Tag;

        private bool _error;
        private Timer _redoTimer;
        private int _tries;
        private const int _webRetries = 4;
        private IAsyncResult _result;

        public WebDownloaderWrapper(string url)
        {
            Url = url;
            PostData = new Dictionary<string, string>();
        }

        public void BeginPost()
        {
            if (IsDownloading) return;

            IsDownloading = true;
            _redoTimer = new Timer(RedoTimerTickPost, null, 1000, 500);

            _result = AsyncWebDownloader.MakeRequest(new Uri(Url), HttpVerb.POST, PostData, Callback);
        }

        public void BeginDelete()
        {
            if (IsDownloading) return;

            IsDownloading = true;
            _redoTimer = new Timer(RedoTimerTickDelete, null, 1000, 500);

            _result = AsyncWebDownloader.MakeRequest(new Uri(Url), HttpVerb.DELETE, PostData, Callback);
        }

        public void BeginDownload()
        {
            if (IsDownloading) return;

            IsDownloading = true;

            _redoTimer = new Timer(RedoTimerTick, null, 1000, 500);

            if (PostData != null && PostData.Count > 0)
                _result = AsyncWebDownloader.MakeRequest(new Uri(Url), HttpVerb.GET, PostData, Callback);
            else
                _result = AsyncWebDownloader.DownloadData(Url, Callback);
        }

        public void BeginDownloadFile()
        {
            if (IsDownloading) return;

            IsDownloading = true;

            _redoTimer = new Timer(RedoTimerTickFile, null, 1000, 500);

            if (PostData != null && PostData.Count > 0)
                _result = AsyncWebDownloader.MakeRequest(new Uri(Url), HttpVerb.GET, PostData, CallbackFile);
            else
                _result = AsyncWebDownloader.DownloadDataFile(Url, CallbackFile);
        }

        void RedoTimerTick(object sender)
        {
            if (IsDownloading) return;

            _redoTimer.Dispose();
            AsyncWebDownloader.ClearAsync(_result);

            if (_tries++ < _webRetries)
                BeginDownload();
        }

        void RedoTimerTickPost(object sender)
        {
            if (IsDownloading) return;

            _redoTimer.Dispose();
            AsyncWebDownloader.ClearAsync(_result);

            if (_tries++ < _webRetries)
                BeginPost();
        }

        void RedoTimerTickDelete(object sender)
        {
            if (IsDownloading) return;

            _redoTimer.Dispose();
            AsyncWebDownloader.ClearAsync(_result);

            if (_tries++ < _webRetries)
                BeginDelete();
        }

        void RedoTimerTickFile(object sender)
        {
            if (IsDownloading) return;

            _redoTimer.Dispose();
            AsyncWebDownloader.ClearAsync(_result);

            try
            {
                if (_tries++ < _webRetries)
                    BeginDownloadFile();
            }
            catch (OutOfMemoryException) { }
        }

        public string Download()
        {
            return PostData != null ? WebDownloader.MakeRequest(new Uri(Url), HttpVerb.GET, PostData) : WebDownloader.DownloadHTML(Url);
        }

        public string Post()
        {
            return PostData != null ? WebDownloader.MakeRequest(new Uri(Url), HttpVerb.POST, PostData) : WebDownloader.DownloadHTML(Url);
        }

        private void Callback(object text, SuccessEventArgs args)
        {
            _error = !args.Success;
            IsDownloading = false;
            if (!args.Success) return;

            Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
            _redoTimer.Dispose();
            if (GetResults != null)
                GetResults.Invoke(this, new TextEventArgs(text.ToString()));
        }

        private void CallbackFile(object mem, SuccessEventArgs args)
        {
            _error = !args.Success;
            IsDownloading = false;

            if (!args.Success) return;

            Thread.CurrentThread.Priority = ThreadPriority.BelowNormal;
            _redoTimer.Dispose();

            for (int i = 0; i < GetImageCallbacks.Count; i++)
            {
                GetImageCallbacks[i]((MemoryStream)mem);
            }

            if (DownloadFileComplete != null)
                DownloadFileComplete(this, new EventArgs());
        }

        public void Dispose()
        {
            if (_result == null) return;

            if (_redoTimer != null)
                _redoTimer.Dispose();
            AsyncWebDownloader.ClearAsync(_result);
        }
    }

    public class AsyncWebDownloader
    {
        private static string userAgent = "Mozilla/5.0 (Windows; U; Windows NT 6.0; en-US) AppleWebKit/534.13 (KHTML, like Gecko) Chrome/9.0.597.107 Safari/534.13";
        private static List<IAsyncResult> _globalList = new List<IAsyncResult>();
        private static int timeout = 15000;
        public static void ClearAsync()
        {
            for (int i = 0; i < _globalList.Count; i++)
            {
                var res = (RequestState)_globalList[i].AsyncState;
                res.Request.Abort();
            }
            _globalList.Clear();
        }

        public static void ClearAsync(IAsyncResult result)
        {
            try
            {
                if (_globalList.Contains(result))
                {
                    var res = (RequestState)result.AsyncState;
                    res.Request.Abort();
                }
                _globalList.Remove(result);
            }
            catch (NullReferenceException) { }
        }

        [ComVisible(true)]
        public delegate void WaitOrTimerCallback(Object state, bool timedOut);

        public static IAsyncResult MakeRequest(Uri url, HttpVerb httpVerb, Dictionary<string, string> args, EventHandler<SuccessEventArgs> callback)
        {
            if (args != null && args.Keys.Count > 0 && httpVerb == HttpVerb.GET)
            {
                url = new Uri(url + RequestUtil.EncodeDictionary(args, true));
            }
            if (args == null)
                args = new Dictionary<string, string>();

            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = httpVerb.ToString();
            request.UserAgent = userAgent;
            request.KeepAlive = false;
            request.AllowAutoRedirect = true;
            request.Timeout = timeout;
            request.Headers.Add("Accept-Encoding", "gzip, deflate");
            if (WebDownloader.useEmptyProxy)
            {
                request.Proxy = GlobalProxySelection.GetEmptyWebProxy();
            }

            if (httpVerb == HttpVerb.POST || httpVerb == HttpVerb.DELETE)
            {
                string postData = RequestUtil.EncodeDictionary(args, false);
                var encoding = new ASCIIEncoding();

                byte[] postDataBytes = encoding.GetBytes(postData);
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = postDataBytes.Length;
                request.Accept =
                    @"image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/vnd.xfdl; version=""6.5.0"", application/x-shockwave-flash, */*";
                request.Method = httpVerb.ToString();
                request.ProtocolVersion = new Version(1, 1);
                request.Headers.Add("Accept-Language: en-us");
                request.Headers.Add("Cache-Control: no-cache");

                var req = new RequestState(request, callback, postDataBytes);
                return request.BeginGetRequestStream(GetRequestStreamCallback, req);
            }

            var requestState = new RequestState(request, callback);
            var ret = request.BeginGetResponse(GetResponseCallback, requestState);
            _globalList.Add(ret);
            return ret;
        }

        public static IAsyncResult PostData(string url, string poststring, CookieManager cookieManager, EventHandler<SuccessEventArgs> callback)
        {
            if (string.IsNullOrEmpty(poststring))
                return null;

            var httpRequest = (HttpWebRequest)WebRequest.Create(url);
            cookieManager.PublishCookies(httpRequest);

            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";
            httpRequest.UserAgent = userAgent;
            httpRequest.AllowAutoRedirect = true;
            httpRequest.KeepAlive = false;
            httpRequest.ProtocolVersion = new Version(1, 1);
            httpRequest.Timeout = timeout;
            httpRequest.AllowWriteStreamBuffering = true;
            if (WebDownloader.useEmptyProxy)
            {
                httpRequest.Proxy = GlobalProxySelection.GetEmptyWebProxy();
            }

            byte[] bytedata = Encoding.UTF8.GetBytes(poststring);
            httpRequest.ContentLength = bytedata.Length;

            var state = new RequestState(httpRequest, callback, bytedata);
            var ret = httpRequest.BeginGetRequestStream(GetRequestStreamCallback, state);
            _globalList.Add(ret);
            return ret;
        }

        public static IAsyncResult DownloadData(string url, EventHandler<SuccessEventArgs> callback)
        {
            var httpRequest = (HttpWebRequest)WebRequest.Create(url);

            httpRequest.Method = "GET";
            httpRequest.ProtocolVersion = new Version(1, 1);
            httpRequest.KeepAlive = false;
            httpRequest.Headers.Add("Accept-Encoding", "gzip, deflate");
            httpRequest.Accept = "image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*";
            httpRequest.UserAgent = userAgent;
            httpRequest.AllowAutoRedirect = true;
            httpRequest.Timeout = timeout;
            if (WebDownloader.useEmptyProxy)
            {
                httpRequest.Proxy = GlobalProxySelection.GetEmptyWebProxy();
            }

            var state = new RequestState(httpRequest, callback);

            var ret = httpRequest.BeginGetResponse(GetResponseCallback, state);
            _globalList.Add(ret);
            return ret;
        }

        public static IAsyncResult DownloadDataFile(string url, EventHandler<SuccessEventArgs> callback)
        {
            var httpRequest = (HttpWebRequest)WebRequest.Create(url);

            httpRequest.Method = "GET";
            httpRequest.ProtocolVersion = new Version(1, 1);
            httpRequest.KeepAlive = false;
            httpRequest.Headers.Add("Accept-Encoding", "gzip, deflate");
            httpRequest.Accept = "image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*";
            httpRequest.UserAgent = userAgent;
            httpRequest.AllowAutoRedirect = true;
            httpRequest.Timeout = timeout;
            if (WebDownloader.useEmptyProxy)
            {
                httpRequest.Proxy = GlobalProxySelection.GetEmptyWebProxy();
            }

            var state = new RequestState(httpRequest, callback);

            IAsyncResult ret = httpRequest.BeginGetResponse(GetResponseCallbackFile, state);
            _globalList.Add(ret);
            return ret;
        }

        public static IAsyncResult DownloadData(string url, CookieManager cookieManager, EventHandler<SuccessEventArgs> callback)
        {
            var httpRequest = (HttpWebRequest)WebRequest.Create(url);
            cookieManager.PublishCookies(httpRequest);

            httpRequest.Method = "GET";
            httpRequest.ProtocolVersion = new Version(1, 1);
            httpRequest.KeepAlive = false;
            httpRequest.Accept = "application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5";
            httpRequest.UserAgent = userAgent;
            httpRequest.AllowAutoRedirect = true;
            httpRequest.Timeout = timeout;
            if (WebDownloader.useEmptyProxy)
            {
                httpRequest.Proxy = GlobalProxySelection.GetEmptyWebProxy();
            }

            var state = new RequestState(httpRequest, callback);
            var ret = httpRequest.BeginGetRequestStream(GetResponseCallback, state);
            _globalList.Add(ret);
            return ret;
        }

        private static void GetResponseCallback(IAsyncResult result)
        {
            // grab the custom state object
            var state = (RequestState)result.AsyncState;
            var request = state.Request;
            // get the Response
            try
            {
                using (var httpWebResponse = (HttpWebResponse)request.EndGetResponse(result))
                using (Stream dataStream = DeflateStream(httpWebResponse))
                using (var reader = new StreamReader(dataStream))
                {
                    string ret = reader.ReadToEnd();
                    state.OnComplete(ret, new SuccessEventArgs(true));
                }
                if (_globalList.Contains(result))
                    _globalList.Remove(result);
            }
            catch (Exception ex)
            {
                string error = "";
                if (ex is WebException && ((WebException)ex).Response != null)
                    using (var response = ((WebException)ex).Response)
                    using (var strm = response.GetResponseStream())
                    using (var reader = new StreamReader(strm))
                        error = reader.ReadToEnd();

                //MessageBox.Show("WebException with message: \"" + ex.Message + "\" occurred " + _webRetries + " times. No data was received");
                //XFControlUtils.LogInfo("WebException with message: \"" + ex.Message + "\" occurred. No data was received. URL: "+request.Address, "WebException");
                //XFControlUtils.LogInfo("Type: Async - " + ex.Message + " - " + ex.StackTrace, "Debug");
                Debug.log("Type: Async / GetResponseCallback - " + ex.Message + " - " + ex.StackTrace, DebugType.ERROR);
                Debug.log(request.Address.ToString(), DebugType.URL);
                state.OnComplete(error, new SuccessEventArgs(false));
                if (_globalList.Contains(result))
                    _globalList.Remove(result);
            }
        }

        private static void GetResponseCallbackFile(IAsyncResult result)
        {
            // grab the custom state object
            var state = (RequestState)result.AsyncState;
            var request = state.Request;
            // get the Response

            try
            {
                using (var httpWebResponse = (HttpWebResponse)request.EndGetResponse(result))
                using (var dataStream = DeflateStream(httpWebResponse))
                {
                    var mem = new MemoryStream();
                    XFGraphics.CopyStream(dataStream, mem);
                    mem.Position = 0;

                    state.OnComplete(mem, new SuccessEventArgs(true));
                }
                if (_globalList.Contains(result))
                    _globalList.Remove(result);
            }
            catch (Exception ex)
            {
                if (ex.Message == "The request was aborted: RequestCanceled.")
                    return;

                var error = new MemoryStream();
                if (ex is WebException && ((WebException)ex).Response != null)
                    using (var response = ((WebException)ex).Response)
                    using (var strm = response.GetResponseStream())
                        XFGraphics.CopyStream(strm, error);

                error.Position = 0;
                //MessageBox.Show("WebException with message: \"" + ex.Message + "\" occurred " + _webRetries + " times. No data was received");
                //XFControlUtils.LogInfo("WebException with message: \"" + ex.Message + "\" occurred. No data was received URL: " + request.Address, "WebException");
                //XFControlUtils.LogInfo("Type: Async/File - " + ex.Message + " - " + ex.StackTrace, "Debug");

                Debug.log("Type: Async / GetResponseCallback / File - " + ex.Message + " - " + ex.StackTrace, DebugType.ERROR);
                Debug.log(request.Address.ToString(), DebugType.URL);

                state.OnComplete(error, new SuccessEventArgs(false));

                if (_globalList.Contains(result))
                    _globalList.Remove(result);
            }
        }

        private static void GetRequestStreamCallback(IAsyncResult result)
        {
            var stateResult = (RequestState)result.AsyncState;
            var httpRequest = stateResult.Request;
            var callback = stateResult.OnComplete;
            var bytedata = stateResult.Data;

            try
            {
                using (var requestStream = httpRequest.EndGetRequestStream(result))
                {
                    requestStream.Write(bytedata, 0, bytedata.Length);
                    requestStream.Close();
                }
            }
            catch (Exception ex)
            {

                var error = new MemoryStream();
                if (ex is WebException && ((WebException)ex).Response != null)
                    using (var response = ((WebException)ex).Response)
                    using (var strm = response.GetResponseStream())
                        XFGraphics.CopyStream(strm, error);

                error.Position = 0;
                //MessageBox.Show("WebException with message: \"" + ex.Message + "\" occurred " + _webRetries + " times. No data was received");
                //XFControlUtils.LogInfo("WebException with message: \"" + ex.Message + "\" occurred. No data was received", "WebException");
                Debug.log("Type: Async / GetResponseCallback / Stream- " + ex.Message + " - " + ex.StackTrace, DebugType.ERROR);
                Debug.log(httpRequest.Address.ToString(), DebugType.URL);
            }

            if (_globalList.Contains(result))
                _globalList.Remove(result);

            try
            {
                var state = new RequestState(httpRequest, callback);
                var res = httpRequest.BeginGetResponse(GetResponseCallback, state);
                _globalList.Add(res);
            }
            catch(ProtocolViolationException){}
            //RegisterWaitForSingleObject(res.AsyncWaitHandle, ScanTimeoutCallback, state, (30 * 1000), true);
        }

        private static void RegisterWaitForSingleObject(WaitHandle waitHandle, WaitOrTimerCallback callback, object state, int timeout, bool exOnlyOnce)
        {
            var thread = new Thread(delegate
                                    {
                                        if (waitHandle.WaitOne(timeout, true))
                                        {
                                            callback(state, true);
                                        }
                                    }) { IsBackground = true };
            thread.Start();
        }

        private static void ScanTimeoutCallback(object state, bool timedOut)
        {
            if (!timedOut) return;

            var reqState = (RequestState)state;
            if (reqState != null)
                reqState.Request.Abort();
        }

        public static Stream DeflateStream(HttpWebResponse response)
        {
            Stream compressedStream = null;
            switch (response.ContentEncoding)
            {
                case "gzip":
                    compressedStream = new GZipInputStream(response.GetResponseStream());
                    break;

                case "deflate":
                    compressedStream = new ZipInputStream(response.GetResponseStream());
                    break;
            }

            if (compressedStream == null) return response.GetResponseStream();

            // Decompress
            var decompressedStream = new MemoryStream();
            int size = 2048;
            var buffer = new byte[2048];
            while (true)
            {
                size = compressedStream.Read(buffer, 0, size);
                if (size > 0)
                    decompressedStream.Write(buffer, 0, size);
                else
                    break;
            }
            decompressedStream.Seek(0, SeekOrigin.Begin);
            return decompressedStream;
        }
    }

    public class WebDownloader
    {
        internal const string ANDCLAUSE = " AND";
        internal const string NEWLINE = "\r\n";
        internal const string PREFIX = "--";
        internal const string VERSION = "1.0";

        private const int _webRetries = 6;

        private static string userAgent = "Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; SLCC1; .NET CLR 2.0.50727; InfoPath.2; MS-RTC LM 8; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET4.0C; .NET4.0E)";

        public static Int32 LoginTimeOut = 10000; // half minute 
        public static bool ResizeImages;
        public static bool useEmptyProxy = true;

        public static string PostData(string url, string poststring, CookieManager cookieManager)
        {
            if (string.IsNullOrEmpty(poststring))
                return null;

            var httpRequest = (HttpWebRequest)WebRequest.Create(url);
            cookieManager.PublishCookies(httpRequest);

            httpRequest.Method = "POST";
            httpRequest.ContentType = "application/x-www-form-urlencoded";
            httpRequest.UserAgent = userAgent;
            httpRequest.AllowAutoRedirect = true;
            httpRequest.KeepAlive = false;
            httpRequest.Timeout = LoginTimeOut;

            byte[] bytedata = Encoding.UTF8.GetBytes(poststring);
            httpRequest.ContentLength = bytedata.Length;

            Stream requestStream = httpRequest.GetRequestStream();
            requestStream.Write(bytedata, 0, bytedata.Length);
            requestStream.Close();


            try
            {
                using (var httpWebResponse = (HttpWebResponse)httpRequest.GetResponse())
                {
                    if (httpWebResponse != null)
                        cookieManager.StoreCookies(httpWebResponse);
                    else
                        return "";

                    using (Stream dataStream = httpWebResponse.GetResponseStream())
                    {
                        var reader = new StreamReader(dataStream);

                        string ret = reader.ReadToEnd();
                        return ret;
                    }
                }
            }
            catch (WebException ex) { }

            return "";
        }

        public static HttpWebResponse GetRawResponse(string url, CookieManager cookieManager)
        {
            for (int i = 0; i < _webRetries; i++)
            {
                try
                {
                    var request = (HttpWebRequest)WebRequest.Create(new Uri(url));
                    request.UserAgent = userAgent;
                    request.Headers.Add("Accept-Encoding", "gzip, deflate");
                    request.Accept = "image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*";
                    request.Timeout = LoginTimeOut;
                    request.Method = "GET";
                    request.KeepAlive = false;
                    if (WebDownloader.useEmptyProxy)
                    {
                        request.Proxy = GlobalProxySelection.GetEmptyWebProxy();
                    }
                    cookieManager.PublishCookies(request);

                    return (HttpWebResponse)request.GetResponse();
                }
                catch (Exception e)
                {
                    try
                    {
                        var request = (HttpWebRequest)WebRequest.Create(new Uri(url));
                        request.UserAgent = userAgent;
                        request.Headers.Add("Accept-Encoding", "gzip, deflate");
                        request.Timeout = LoginTimeOut;
                        request.Method = "GET";
                        request.KeepAlive = false;
                        if (WebDownloader.useEmptyProxy)
                        {
                            request.Proxy = GlobalProxySelection.GetEmptyWebProxy();
                        }
                        cookieManager.PublishCookies(request);
                        Debug.log("Type: GetRawResponse 1 - " + e.Message + " - " + e.StackTrace, DebugType.ERROR);
                        Debug.log(request.Address.ToString(), DebugType.URL);
                        return (HttpWebResponse)request.GetResponse();
                    }
                    catch (Exception ex)
                    {
                        string error = "";
                        if (ex is WebException && ((WebException)ex).Response != null)
                        {
                            using (var response = ((WebException)ex).Response)
                            using (var strm = response.GetResponseStream())
                            using (var reader = new StreamReader(strm))
                            {
                                error = reader.ReadToEnd();
                                Debug.log("Type: GetRawResponse 2- " + ex.Message + " - " + ex.StackTrace + "\n\r\n\r" + error, DebugType.ERROR);
                                Debug.log(response.ResponseUri.ToString(), DebugType.URL);
                                //XFControlUtils.LogInfo("Type: RawResponse - " + ex.Message + " - " + ex.StackTrace, "Debug");
                            }
                        }
                    }
                }
            }
            return null;
        }

        /// <summary>
        /// Downloads a JSON string from a given URL.
        /// </summary>
        /// <param name="url">The URL for the JSON object.</param>
        /// <returns>A string in JSON notation.</returns>
        public static string DownloadJSON(string url)
        {
            return DownloadJSON(url, new CookieManager());
        }

        public static string DownloadJSON(string url, CookieManager cookieManager)
        {
            string ret = "";
            try
            {
                using (HttpWebResponse response = GetRawResponse(url, cookieManager))
                {
                    if (response != null)
                        cookieManager.StoreCookies(response);
                    else return ret;

                    using (Stream dataStream = AsyncWebDownloader.DeflateStream(response))
                    using (var reader = new StreamReader(dataStream))
                        ret = reader.ReadToEnd();
                }
            }
            catch (Exception ex)
            {
                string error = "";
                if (ex is WebException)
                    using (var response = (HttpWebResponse)((WebException)ex).Response)
                    using (var strm = AsyncWebDownloader.DeflateStream(response))
                    using (var reader = new StreamReader(strm))
                        error = reader.ReadToEnd();
            }
            return ret;
        }

        public static string DownloadHTML(string url)
        {
            return DownloadJSON(url, new CookieManager());
        }

        public static string DownloadHTML(string url, CookieManager cookieManager)
        {
            return DownloadJSON(url, cookieManager);
        }

        public static string GetFileQueryResponse(IEnumerable<KeyValuePair<string, string>> parameterDictionary,
                                                  FileSystemInfo uploadFile, string contentType, string url, Delegate callBack)
        {
            string responseData;

            string boundary = DateTime.Now.Ticks.ToString("x", CultureInfo.InvariantCulture);
            string sRequestUrl = url;
            if (contentType == "video/avi")
            {
                sRequestUrl = sRequestUrl.Replace("api.", "api-video.");
            }

            // Build up the post message header
            var sb = new StringBuilder();
            foreach (var kvp in parameterDictionary)
            {
                sb.Append(PREFIX).Append(boundary).Append(NEWLINE);
                sb.Append("Content-Disposition: form-data; name=\"").Append(kvp.Key).Append("\"");
                sb.Append(NEWLINE);
                sb.Append(NEWLINE);
                sb.Append(kvp.Value);
                sb.Append(NEWLINE);
            }
            sb.Append(PREFIX).Append(boundary).Append(NEWLINE);
            sb.Append("Content-Disposition: form-data; name=\"source\"; filename=\"").Append(uploadFile.Name).Append("\"").Append(NEWLINE);
            sb.Append("Content-Type: ").Append(contentType).Append(NEWLINE).Append(NEWLINE);


            byte[] fileData;

            if (XFGraphics.IsImage(uploadFile.FullName))
                fileData = getImageBytes(uploadFile.FullName, ResizeImages);
            else
            {
                using (FileStream file = File.Open(uploadFile.FullName, FileMode.Open))
                {
                    fileData = new byte[file.Length];
                    file.Read(fileData, 0, fileData.Length);
                }
            }

            byte[] postHeaderBytes = Encoding.UTF8.GetBytes(sb.ToString());
            byte[] boundaryBytes = Encoding.UTF8.GetBytes(String.Concat(NEWLINE, PREFIX, boundary, PREFIX, NEWLINE));

            var webrequest = (HttpWebRequest)WebRequest.Create(sRequestUrl);

            webrequest.Accept = "*/*";
            webrequest.ContentLength = postHeaderBytes.Length + fileData.Length + boundaryBytes.Length;
            webrequest.ContentType = String.Concat("multipart/form-data; boundary=", boundary);
            webrequest.Method = "POST";
            webrequest.KeepAlive = false;
            webrequest.Timeout = LoginTimeOut;
            webrequest.AllowWriteStreamBuffering = true;

            using (Stream requestStream = webrequest.GetRequestStream())
            {
                requestStream.Write(postHeaderBytes, 0, postHeaderBytes.Length);

                int i = 0;
                const int ammount = 4096;
                int length = fileData.Length;
                while (length > 0)
                {
                    if (length - ammount > 0)
                        requestStream.Write(fileData, i++ * ammount, ammount);
                    else
                        requestStream.Write(fileData, i++ * ammount, length);

                    length -= ammount;

                    if (callBack != null && callBack is EventHandler<PercentCompleteArgs>)
                    {
                        double diff = (fileData.Length - length) / (double)fileData.Length;
                        diff *= 100;
                        ((EventHandler<PercentCompleteArgs>)callBack)(null, new PercentCompleteArgs((int)diff));
                    }
                }
                requestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
            }
            try
            {
                using (var response = (HttpWebResponse)webrequest.GetResponse())
                using (var resp = response.GetResponseStream())
                using (var streamReader = new StreamReader(resp))
                {
                    responseData = streamReader.ReadToEnd();
                }
            }
            catch (WebException ex)
            {
                using (var response = ex.Response)
                using (var strm = response.GetResponseStream())
                using (var readStream = new StreamReader(strm))
                    responseData = readStream.ReadToEnd(); //This way wee can see the error message from facebook
            }

            return responseData;
        }

        public static string MakeRequest(Uri url, HttpVerb httpVerb, Dictionary<string, string> args)
        {
            if (args != null && args.Keys.Count > 0 && httpVerb == HttpVerb.GET)
            {
                url = new Uri(url + RequestUtil.EncodeDictionary(args, true));
            }
            if (args == null)
                args = new Dictionary<string, string>();

            var request = (HttpWebRequest)WebRequest.Create(url);
            request.Method = httpVerb.ToString();
            request.UserAgent = userAgent;
            request.Timeout = LoginTimeOut;
            request.KeepAlive = false;
            request.AllowAutoRedirect = true;
            if (WebDownloader.useEmptyProxy)
            {
                request.Proxy = GlobalProxySelection.GetEmptyWebProxy();
            }
            request.Headers.Add("Accept-Encoding", "gzip, deflate");

            if (httpVerb == HttpVerb.POST || httpVerb == HttpVerb.DELETE)
            {
                string postData = RequestUtil.EncodeDictionary(args, false);
                var encoding = new ASCIIEncoding();

                byte[] postDataBytes = encoding.GetBytes(postData);
                request.ContentType = "application/x-www-form-urlencoded";
                request.ContentLength = postDataBytes.Length;
                request.Accept =
                    @"image/gif, image/jpeg, image/pjpeg, application/x-ms-application, application/vnd.ms-xpsdocument, application/xaml+xml, application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, application/vnd.xfdl; version=""6.5.0"", application/x-shockwave-flash, */*";
                request.Method = httpVerb.ToString();
                request.ProtocolVersion = new Version(1, 1);
                request.Headers.Add("Accept-Language: en-us");
                request.Headers.Add("Cache-Control: no-cache");

                using (Stream requestStream = request.GetRequestStream())
                    requestStream.Write(postDataBytes, 0, postDataBytes.Length);
            }

            try
            {
                using (var response = (HttpWebResponse)request.GetResponse())
                using (var resp = AsyncWebDownloader.DeflateStream(response))
                using (var reader = new StreamReader(resp))
                    return reader.ReadToEnd();
            }
            catch (WebException e)
            {
                //XFControlUtils.LogInfo("Type: MakeRequest - " + e.Message + " - " + e.StackTrace,"Debug");
                Debug.log("Type: MakeRequest- " + e.Message + " - " + e.StackTrace, DebugType.ERROR);
                Debug.log(request.Address.ToString(), DebugType.URL);
                using (var response = e.Response)
                    if (response != null)
                        using (var strm = response.GetResponseStream())
                        using (var resp = new StreamReader(strm))
                        {
                            string error = resp.ReadToEnd();
                            return error;
                        }
                return "";
            }
        }

        private static byte[] getImageBytes(string fileName, bool resize)
        {
            var fileData = new byte[0];

            if (!resize)
            {
                using (FileStream file = File.Open(fileName, FileMode.Open))
                {
                    fileData = new byte[file.Length];
                    file.Read(fileData, 0, fileData.Length);
                    return fileData;
                }
            }
            using (var ms = new MemoryStream())
            {
                try
                {
                    using (var image = XFBitmapFactory.GetBitmap(fileName))
                    {
                        if (image.Width < 720 && image.Height < 720)
                        {
                            ImageFormat format = ImageFormat.Png;
                            image.Save(ms, format);
                            fileData = ms.ToArray();
                            return fileData;
                        }
                    }
                }
                catch (OutOfMemoryException) { }

                using (Bitmap image = XFGraphics.CreateThumbnailFromFile(fileName, 720, 720))
                {
                    ImageFormat format = ImageFormat.Png;
                    image.Save(ms, format);
                    fileData = ms.ToArray();
                }
            }
            return fileData;
        }
    }
}